#include "vspage.h"

#include <gtk/gtkclipboard.h>
#include <strings.h>
#include "marshal.h"

static GtkWidgetClass *parent_class = NULL;

static gboolean is_rect_empty(PopplerRectangle *rect)
{
	return rect->x1 == rect->x2 && rect->y1 == rect->y2;
}

static void render(VSPage *self)
{
	if(!self->pixmap)
		return;

	gdouble width, height;
	poppler_page_get_size(self->page, &width, &height);

	cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(self->pixmap));
	cairo_scale(cr, self->scale, self->scale);

	//fill with white
	cairo_set_source_rgb(cr, 1,1,1);
	cairo_rectangle(cr, 0,0, width,height);
	cairo_fill(cr);

	//render page
	cairo_save(cr);
	poppler_page_render(self->page, cr);

	cairo_destroy(cr);
	gtk_widget_queue_draw(GTK_WIDGET(self));
}

static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event)
{
	VSPage *self = VSPAGE(widget);
	GdkGC *gc = gdk_gc_new(GDK_DRAWABLE(widget->window));
	PopplerColor black={0,0,0}, white = {G_MAXUINT16, G_MAXUINT16, G_MAXUINT16};

	gdk_draw_drawable(GDK_DRAWABLE(widget->window), gc, GDK_DRAWABLE(self->pixmap),
					  0,0, self->dst.x, self->dst.y, -1,-1);

	//render selection if not empty
	if(!is_rect_empty(&self->selection)){
		cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
		cairo_translate(cr, self->dst.x, self->dst.y);
		cairo_scale(cr, self->scale, self->scale);
		poppler_page_render_selection(self->page, cr, &self->selection, NULL, POPPLER_SELECTION_WORD, &white, &black);
		cairo_destroy(cr);
	}

	g_object_unref(gc);
	return TRUE;	
}

static gboolean adjustment_update(gpointer data)
{
	VSPage *self = data;
	if(!self->adj)
		return FALSE;

	GtkWidget *w = GTK_WIDGET(self);
	int window_h;
	int pixmap_h;

	gdk_drawable_get_size(GDK_DRAWABLE(w->window), NULL, &window_h);
	gdk_drawable_get_size(GDK_DRAWABLE(self->pixmap), NULL, &pixmap_h);

	self->adj->upper = pixmap_h;
	self->adj->page_size = window_h;
	gtk_adjustment_changed(self->adj);

	if(pixmap_h > window_h)
		self->adj->value = self->dst.y;
	else
		self->adj->value = 0;
	gtk_adjustment_value_changed(self->adj);

	return FALSE;
}

static void adjustment_changed(VSPage *self, GtkAdjustment *adj)
{
	if(adj->page_size < adj->upper){
		self->dst.y = -adj->value;
		gtk_widget_queue_draw(GTK_WIDGET(self));
	}
}

static void adjustment_set(VSPage *self, GtkAdjustment *unused, GtkAdjustment *adj)
{
	self->adj = adj;
	if(adj){
		g_signal_connect_swapped(adj, "value-changed", 
								 G_CALLBACK(adjustment_changed), self);
	}
}

static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event)
{
	VSPage *self = VSPAGE(widget);

	gdouble page_w, page_h;
	poppler_page_get_size(self->page, &page_w, &page_h);

	//calc scale
	gdouble sx = widget->allocation.width / page_w;
	gdouble sy = widget->allocation.height/ page_h;
	if(self->zmode == ZOOM_TO_PAGE)
		self->scale = MIN(sx, sy);
	else
		self->scale = sx;

	//create pixmap if needed
	int scaled_w = page_w*self->scale;
	int scaled_h = page_h*self->scale;
	if(self->pixmap)
		g_object_unref(self->pixmap);
	self->pixmap = gdk_pixmap_new(widget->window, scaled_w, scaled_h, -1);

	//init dst point
	self->dst = (Point){0,0};
	if(widget->allocation.height >= scaled_h){
		self->dst.x = (widget->allocation.width - scaled_w) / 2;
		self->dst.y = (widget->allocation.height - scaled_h) / 2;
	}

	render(self);
	adjustment_update(self);

	return TRUE;
}

static gboolean press_event(GtkWidget *widget, GdkEventButton *event)
{
	VSPage *self = VSPAGE(widget);

	GdkCursor *cursor = gdk_cursor_new (GDK_XTERM);
	gdk_window_set_cursor (widget->window, cursor);
	gdk_flush ();

	if(event->type == GDK_BUTTON_PRESS && event->button == 1){
		self->selection.x2 = self->selection.x1 = (event->x - self->dst.x) / self->scale;
		self->selection.y2 = self->selection.y1 = (event->y - self->dst.y) / self->scale;
		gtk_widget_queue_draw(GTK_WIDGET(self));
	}

	return TRUE;
}

static gboolean motion_event (GtkWidget *widget, GdkEventMotion *event)
{
	VSPage *self = VSPAGE(widget);

	self->selection.x2 = (event->x - self->dst.x) / self->scale;
	self->selection.y2 = (event->y - self->dst.y) / self->scale;
	gtk_widget_queue_draw(GTK_WIDGET(self));

	gdk_event_request_motions(event);

	return TRUE;
}

static gboolean release_event(GtkWidget *widget, GdkEventButton *event)
{
	VSPage *self = VSPAGE(widget);

	gdk_window_set_cursor (widget->window, NULL);
	gdk_flush ();

	gdouble height;
	poppler_page_get_size(self->page, NULL, &height);
	PopplerRectangle rect = {self->selection.x1, height - self->selection.y2, 
							 self->selection.x2, height - self->selection.y1};
	gchar *text = NULL;
	GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
	text = poppler_page_get_text(self->page, POPPLER_SELECTION_WORD, &rect);
	if(text)
		gtk_clipboard_set_text(cb, text, -1);
	g_free(text);

	return TRUE;
}

static void finalize(GObject *obj)
{
	VSPage *self = VSPAGE(obj);
	
	if(self->pixmap)
		g_object_unref(self->pixmap);
	if(self->page)
		g_object_unref(self->page);

	G_OBJECT_CLASS(parent_class)->finalize(obj);
}

G_DEFINE_TYPE(VSPage, vspage, GTK_TYPE_DRAWING_AREA);
static void vspage_class_init(VSPageClass *class)
{
	G_OBJECT_CLASS(class)->finalize = finalize;
	GTK_WIDGET_CLASS(class)->expose_event = expose_event;
	GTK_WIDGET_CLASS(class)->configure_event = configure_event;
	GTK_WIDGET_CLASS(class)->motion_notify_event = motion_event;
	GTK_WIDGET_CLASS(class)->button_press_event = press_event;
	GTK_WIDGET_CLASS(class)->button_release_event = release_event;
	class->set_scroll_adjustments = adjustment_set;
	parent_class = g_type_class_peek_parent (class);

	GTK_WIDGET_CLASS(class)->set_scroll_adjustments_signal =
		g_signal_new ("set_scroll_adjustments",
					  G_OBJECT_CLASS_TYPE (class),
					  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
					  G_STRUCT_OFFSET (VSPageClass, set_scroll_adjustments),
					  NULL, NULL,
					  g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
					  G_TYPE_NONE, 2,
					  GTK_TYPE_ADJUSTMENT,
					  GTK_TYPE_ADJUSTMENT);
}

static void vspage_init(VSPage *self)
{
	self->scale = 1;
	gtk_widget_add_events(GTK_WIDGET(self), GDK_SCROLL_MASK|GDK_BUTTON1_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK|
						  GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_KEY_PRESS_MASK);
	GTK_WIDGET_SET_FLAGS(GTK_WIDGET(self), GTK_CAN_FOCUS);	
}

PopplerPage* vspage_get_page(VSPage *self)
{
	return self->page;
}

void vspage_set_page(VSPage *self, PopplerPage* page)
{
	if(self->page)
		g_object_unref(self->page);
	self->page = page;
	g_object_ref(self->page);
	bzero(&self->selection, sizeof(self->selection));
	render(self);
}

void vspage_set_selection(VSPage *self, PopplerRectangle *sel)
{
	self->selection = *sel;
	gtk_widget_queue_draw(GTK_WIDGET(self));
}

void vspage_set_zoom_mode(VSPage *self, ZoomMode new_mode)
{
	self->zmode = new_mode;

	GtkWidget *widget = GTK_WIDGET(self);
	if(widget->window)
		configure_event(widget, NULL);
}
